package gl.java.umsp.websocket;

import gl.java.umsp.UmspConnectionHandler;
import gl.java.umsp.room.RoomConnectionHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import lombok.extern.log4j.Log4j;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class UmspWebSocketService {

    private int port;
    private Class<? extends ChannelInboundHandlerAdapter>[] mWebSocketHandlerClazz;
    private String mWebSocketUri;


    public UmspWebSocketService(int port, String webSocketUri, Class<? extends ChannelInboundHandlerAdapter>... handler) {
        this.port = port;
        this.mWebSocketHandlerClazz = handler;
        this.mWebSocketUri = webSocketUri;

    }

    public void run() throws InterruptedException {

        EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap(); // (2)
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class) // (3)
                    .childHandler(new ChannelInitializer() {
                        @Override
                        protected void initChannel(Channel ch) throws Exception {
                            ch.pipeline().addLast(new UmspConnectionHandler(),
                                    new HttpServerCodec(),
                                    new HttpObjectAggregator(64 * 1024),
                                    new ChunkedWriteHandler(),
                                    new HttpRequestHandler(),
                                    new WebSocketServerProtocolHandler(mWebSocketUri)
                            );
                            for (Class<? extends ChannelInboundHandlerAdapter> webSocketHandlerClazz
                                    : mWebSocketHandlerClazz) {
                                ch.pipeline().addLast(webSocketHandlerClazz.newInstance());
                            }

                        }
                    })  //(4)
                    .option(ChannelOption.SO_BACKLOG, 128)          // (5)
                    .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

            log.info("=========================================");
            log.info("== WebSocketServer is running at " + port + " ==");
            log.info("=========================================");

            // 绑定端口，开始接收进来的连接
            ChannelFuture f = b.bind(port).sync(); // (7)

            // 等待服务器  socket 关闭 。
            // 在这个例子中，这不会发生，但你可以优雅地关闭你的服务器。
            f.channel().closeFuture().sync();

        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();

            log.info("WebSocketServer is closed");
        }
    }
}